home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume6 / copt < prev    next >
Encoding:
Text File  |  1989-03-07  |  29.6 KB  |  1,021 lines

  1. Newsgroups: comp.sources.misc
  2. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  3. Subject: v06i062: A library for command argument parsing for C
  4. Message-Id: <705@gould.doc.ic.ac.uk>
  5. Distribution: world
  6. Organization: Dept. of Computing, Imperial College, London, UK.
  7. Reply-To: Nigel Perry <np@doc.ic.ac.uk>
  8.  
  9. Posting-number: Volume 6, Issue 62
  10. Submitted-by: Nigel Perry <np@doc.ic.ac.uk>
  11. Archive-name: copt
  12.  
  13. [ Getopt is here to stay, but for those who don't like it...
  14.   Manpages would help.  --r$  ]
  15.  
  16. [This is silly.  One person thinks to overcome the trend toward standardized
  17. option processing?  Easier to convert the Ayatollah to Catholicism.  And has
  18. the author given any thought to how one would use a wildcard in his string
  19. options?  (hint:  "dd if=ar*xy of=/dev/rmt8" fails.)  ++bsa]
  20.  
  21.     This is a library which contains routines to perform nice
  22. parsing of command lines for C programs, no more "-letter" options
  23. for everything now you can have +option, -option, option=value etc.
  24.  
  25.                     Nigel
  26.  
  27. --- cut here ---
  28. #! /bin/sh
  29. # This is a shell archive, meaning:
  30. # 1. Remove everything above the #! /bin/sh line.
  31. # 2. Save the resulting text in a file.
  32. # 3. Execute the file with /bin/sh (not csh) to create the files:
  33. #    README
  34. #    abbrev.c
  35. #    cset.c
  36. #    cset.d
  37. #    cset.h
  38. #    d_optable.c
  39. #    demo.c
  40. #    makefile
  41. #    not_kwd.c
  42. #    numarg.c
  43. # This archive created: Fri Mar  3 12:57:09 1989
  44. export PATH; PATH=/bin:$PATH
  45. if test -f 'README'
  46. then
  47.     echo shar: will not over-write existing file "'README'"
  48. else
  49. cat << \SHAR_EOF > 'README'
  50. Description
  51. ===========
  52.  
  53. A radical departure fro Unix - proper command line parsing!
  54.  
  55. This is a library to replace getopt() or simple "-" option processing by a program.
  56. It provies automatic scanning switch options, + to turn on, - to turn off; string
  57. valued options, e.g. file=../fred, and numeric options - these may be in any base
  58. between 2 and 36 or autobase, which follows the C convention for constants. Multiple
  59. numeric value options are also parsed, e.g. tabs=4,7,23, and return as a vector the
  60. first element being the number of values found. As well as parsing options the library
  61. provides for displaying a table of options on request. Run the demo program - which is
  62. the only "proper" documentation (sorry, but I've never got around to it).
  63.  
  64. Installation
  65. ============
  66.  
  67. Run make. Copy cset.a & cset.h to suitable directories.
  68.  
  69. History
  70. =======
  71.  
  72. Many years ago I used a Honeywell GCOS/TSS system and the B language from the University of
  73. Waterloo, Ontario. The B language came with a command line parser library, bset(). I extended
  74. that library, adding options to print the argument table etc. and standardised all the commands
  75. on the TSS system (plus some other major hacking, but enough said...). Later I rewrote the whole
  76. library from scratch in C for Unix.
  77.  
  78. Acknowlegements
  79. ===============
  80.  
  81. To the people at Waterloo for the original idea.
  82. To by boss at the time - which is the reason for the detailed copyright notice
  83. [We had an offical agreement, anything I wrote and distributed could either be
  84. sold at a price set by him with me getting a percentage, or if I disagreed with
  85. his proposed price I could overrule and distribe it free...]
  86.  
  87. If anybody writes some proper manual pages, please send me a copy!
  88.  
  89. Nigel Perry
  90. Dept of Computing
  91. Imperial College
  92. London SW7
  93. England
  94.  
  95. np@uk.ac.ic.doc
  96. SHAR_EOF
  97. fi # end of overwriting check
  98. if test -f 'abbrev.c'
  99. then
  100.     echo shar: will not over-write existing file "'abbrev.c'"
  101. else
  102. cat << \SHAR_EOF > 'abbrev.c'
  103. static char rcsid[] = "$Header";
  104. /* $Log:    abbrev.c,v $
  105.  * Revision 1.1  89/03/03  12:53:21  np
  106.  * Initial revision
  107.  * 
  108.  */
  109.  
  110. /*
  111.  * Copyright (C) 1985-1989 Nigel Perry
  112.  *
  113.  * This program is distributed without any warranty.
  114.  * The author and and distributors accept no resposibility
  115.  * to anyone for the consequence of using this program or
  116.  * whether it serves any particular purpose or works at all,
  117.  * unless they say so in writing.
  118.  *
  119.  * Everyone is granted permission to copy, modify and
  120.  * redistribute this program, but only provided that:
  121.  *
  122.  * 1) They do so without financial or material gain.
  123.  *
  124.  * 2) The copyright notice and this notice are preserved on
  125.  *    all copies.
  126.  *
  127.  * 3) The original source is preserved in any redistribution.
  128.  *
  129.  * I hope you find this program useful!
  130.  *
  131.  */
  132.  
  133. #define repeat while(1)  /* try to make C look high level! */
  134.  
  135. #define REQUIRED 32
  136. #define OPTIONAL 1
  137.  
  138. /* check if string is a valid abbreviation of pattern
  139.  *
  140.  * In a pattern:
  141.  *   a thru z and _ are optional
  142.  *   other characters must be present
  143.  *
  144.  * Return:
  145.  *  -1 : no match
  146.  *  n = REQUIRED * (number of required matches) + OPTIONAL * (number of optional characters matched)
  147.  *
  148.  * e.g
  149.  *    Pattern        String        Return
  150.  *    LineFeed    lf        2 * REQUIRED
  151.  *    Longform    lf        1 * REQUIRED + 1 * OPTIONAL
  152.  * => lf is a better match for LineFeed
  153.  *
  154.  * The higher the result the better the match.
  155.  *
  156.  */
  157.  
  158. int abbrev(pat, str) register char *pat, *str;
  159. {  register char c, pc;
  160.    register int score, sc;
  161.  
  162.    score = 0;
  163.    c = *str++;
  164.    if(c >= 'a' && c <= 'z') c += 'A' - 'a';
  165.    repeat
  166.       if( (pc = *pat++) == '\0' )        /* this SHOULD be a case... */
  167.      return( c == '\0' ? score : -1 );
  168.       else if(pc == '_') /* optional */
  169.       {  if(c != '_') continue;            /* try next pattern character */
  170.      if( (sc = abbrev(pat, str)) >= 0 ) return(score + OPTIONAL + sc);
  171.      /* rest of match failed, try next pattern char */
  172.       }
  173.       else if(pc >= 'a' && pc <= 'z')        /* = case 'a' :: 'z', B rules! */
  174.       {  if( (c - 'A' + 'a') != pc ) continue;    /* no match, try next pat */
  175.      if( (sc = abbrev(pat, str)) >= 0 ) return(score + OPTIONAL + sc);
  176.       }
  177.       else /* must match */
  178.       {  if(c != pc) return(-1);
  179.      score += REQUIRED;
  180.      c = *str++;                /* get next str char */
  181.      if(c >= 'a' && c <= 'z') c += 'A' - 'a';
  182.       }
  183. }
  184. SHAR_EOF
  185. fi # end of overwriting check
  186. if test -f 'cset.c'
  187. then
  188.     echo shar: will not over-write existing file "'cset.c'"
  189. else
  190. cat << \SHAR_EOF > 'cset.c'
  191. static char rcsid[] = "$Header: cset.c,v 1.1 89/03/03 12:53:22 np Exp $";
  192. /* $Log:    cset.c,v $
  193.  * Revision 1.1  89/03/03  12:53:22  np
  194.  * Initial revision
  195.  * 
  196.  */
  197.  
  198. /*
  199.  * Copyright (C) 1985-1989 Nigel Perry
  200.  *
  201.  * This program is distributed without any warranty.
  202.  * The author and and distributors accept no resposibility
  203.  * to anyone for the consequence of using this program or
  204.  * whether it serves any particular purpose or works at all,
  205.  * unless they say so in writing.
  206.  *
  207.  * Everyone is granted permission to copy, modify and
  208.  * redistribute this program, but only provided that:
  209.  *
  210.  * 1) They do so without financial or material gain.
  211.  *
  212.  * 2) The copyright notice and this notice are preserved on
  213.  *    all copies.
  214.  *
  215.  * 3) The original source is preserved in any redistribution.
  216.  *
  217.  * I hope you find this program useful!
  218.  *
  219.  */
  220.  
  221. #include <stdio.h>
  222. #include "cset.h"
  223. #include "cset.d"
  224.  
  225. #define strequ(a, b) (!strcmp(a,b))
  226.  
  227. extern char *malloc(), *realloc();
  228.  
  229. /* args:
  230.  *    args     - array of char *'s to args (i.e. argv from main())
  231.  *    helpexit - exit program iff help given
  232.  * return:
  233.  *    array of _opt_desc describing options
  234.  */
  235.  
  236. _opt_desc *_cset(args, helpexit) char **args;
  237. {  char *name, *value, *info, *offset, *left, *right;
  238.    int i, type, maxopt, max, score, helpgiven, len;
  239.    _opt_entry *help_entry, *cp, *p;
  240.    int do_help_entry;
  241.    int super;
  242.    _opt_desc *result, *resvec;
  243.    extern _opt_entry _optable[];
  244.    extern char *index();
  245.  
  246.    helpgiven = do_help_entry = 0;
  247.    super = getuid() == 0; /* we be a super user */
  248.  
  249.    /* find largest option number, last entry in _optable is help_entry */
  250.    help_entry = _optable;
  251.    maxopt = 1;
  252.    while( help_entry->_opt_pat ) { help_entry++; maxopt++; }
  253.  
  254.    /* get a vector for the result */
  255.    for(i = 0; args[i]; i++); /* required length */
  256.    resvec = result = (_opt_desc *) malloc( sizeof(_opt_desc) * (i + 1) );
  257.    result[i]._opt_num = -1; /* flag end */
  258.  
  259.    for(i = 0; info = args[i]; i++)
  260.    {  if(*info == '+') /* +option */
  261.       {  name = info + 1;
  262.      type = PLUS_KWD;
  263.      result->_opt_type = '+';
  264.       }
  265.       else if(*info == '-') /* -option */
  266.       {  name = info + 1;
  267.      type = DASH_KWD;
  268.      result->_opt_type = '-';
  269.       }
  270.       else if(value = index(info, '='))
  271.       {  if(value != info) /* option=value */
  272.      {  name = info;
  273.         *value++ = '\0'; /* add eos over =, bump ptr to value */
  274.         if( *value == '\0' && args[i+1] != 0 ) /* option= value */
  275.            value = args[++i];
  276.         type = (SVAL_KWD | NVAL_KWD);
  277.         result->_opt_type = '=';
  278.      }
  279.      else /* =<something> */
  280.      {  value++; /* ptr to <something> */
  281.         if( strequ(value, "=") ) /* display complete option table */
  282.         {  d_optable(0, (_opt_entry *)0, super);
  283.            helpgiven = 1;
  284.            continue; /* don't wan't to scan optable, go direct to next */
  285.         }
  286.         else if( strequ(value, "+") ) /* display long optable */
  287.         {  d_optable(1, (_opt_entry *)0, super);
  288.            helpgiven = 1;
  289.            continue; /* don't wan't to scan optable, go direct to next */
  290.         }
  291.         else if( strequ(value, "#") ) /* display help info */
  292.         {  if(help_entry->_opt_types == 0)
  293.           fprintf(stderr, "Sorry, no help available, see manual\n");
  294.            else
  295.           do_help_entry  = 1;
  296.            helpgiven = 1;
  297.            continue; /* don't wan't to scan optable, go direct to next */
  298.         }
  299.         else /* see if <something> is an option */
  300.         {  /* scan optable for match */
  301.            max = 0; /* best match so far */
  302.            cp = (_opt_entry *)0;
  303.            p = _optable;
  304.            while( p->_opt_pat )
  305.            {  if( super || !(p->_opt_types & PRIV_BIT) )
  306.           {  if( (score = abbrev(p->_opt_pat, value)) > max )
  307.              {  max = score;
  308.             cp = p;
  309.              }
  310.           }
  311.           p++;
  312.            }
  313.            if(cp) /* got a match - print help message */
  314.            {  _opt_adorn(cp->_opt_types, &left, &right);
  315.           len = strlen(left) + strlen(cp->_opt_pat) + strlen(right);
  316.           fprintf(stderr, "%*s%s%s%s%*s%s\n", TAB1, "",
  317.                left, cp->_opt_pat, right,
  318.                TAB3 - (len + TAB1), "",
  319.                cp->_opt_help == (char *)0 ? "(see manual)" : cp->_opt_help);
  320.           helpgiven = 1;
  321.           continue; /* don't wan't to scan optable */
  322.            }
  323.            else /* treat as possible BLNK_KWD */
  324.            {  name = info;
  325.           type = BLNK_KWD | (i == 0 ? COMM_KWD : 0);
  326.           result->_opt_type = ' ';
  327.            }
  328.         }
  329.      }
  330.       }
  331.       else
  332.       {  name = info;
  333.      type = BLNK_KWD | (i == 0 ? COMM_KWD : 0 );
  334.      result->_opt_type = ' ';
  335.       }
  336.  
  337.       /* scan optable for match */
  338.       max = 0; /* best match so far */
  339.       cp = (_opt_entry *)0;
  340.       p = _optable;
  341.       while( p->_opt_pat )
  342.       {  if( super || !(p->_opt_types & PRIV_BIT) ) /* check for PRIV_KWD */
  343.      {  if( p->_opt_types & type ) /* compatible type? */
  344.         {  if( (score = abbrev(p->_opt_pat, name)) > max )
  345.            {  /* better match */
  346.           max = score;
  347.           cp = p;
  348.            }
  349.         }
  350.      }
  351.      p++;
  352.       }
  353.       if(cp)
  354.       {  result->_opt_num = (int)(cp - _optable) + 1;
  355.      switch( cp->_opt_types & type )
  356.      {  case SVAL_KWD:
  357.            result->_opt.sval = value;
  358.            break;
  359.         case NVAL_KWD:
  360.            if( read_num(value, cp->_opt_types, result) == 0 )
  361.            {  result->_opt_num = maxopt + 1; /* read_num failed */
  362.           result->_opt.sval = info; /* return 'looks like' */
  363.            }
  364.            break;
  365.      }
  366.       }
  367.       else if( type == BLNK_KWD )
  368.       {  result->_opt_num = 0; /* flag as a simple string */
  369.      result->_opt.sval = info;
  370.       }
  371.       else
  372.       {  result->_opt_num = maxopt + 1; /* flag as looks like an option */
  373.      result->_opt.sval = info;
  374.       }
  375.       result++; /* bump ptr */
  376.    }
  377.    result->_opt_num = -1; /* flag end */
  378.  
  379.    /* should we do help_entry */
  380.    if( do_help_entry ) switch( help_entry->_opt_types )
  381.    {  case OPT_LIST:
  382.      list_file(help_entry->_opt_help);
  383.      break;
  384.       case OPT_EXEC:
  385.      system(help_entry->_opt_help);
  386.      break;
  387.    }
  388.  
  389.    /* see if we should exit... */
  390.    if( helpexit && helpgiven ) exit(0);
  391.    return(resvec);
  392. }
  393.  
  394. #define CHUNK (sizeof(int) * 10)
  395.  
  396. static read_num(str, type, result) char *str; _opt_desc *result;
  397. {  int base, flag, offset, *mvals, maxnums, i;
  398.  
  399.    flag = 0; /* dont skip leading junk */
  400.    offset = 0;
  401.    base = type & BASE_MASK;
  402.    if( !(type & MVAL_BIT) ) /* single number */
  403.    {  result->_opt.nval = numarg(str, &offset, base, &flag);
  404.       if( flag == 0 || str[offset] != '\0' ) /* bad num or terminator */
  405.      return(0); /* fail */
  406.    }
  407.    else /* multivalued */
  408.    {  maxnums = CHUNK;
  409.       mvals = (int *)malloc(CHUNK);
  410.       i = 1; /* mvals[0] will be number of numbers */
  411.       do
  412.       {  if(i == maxnums) /* run out of space */
  413.      {  maxnums += CHUNK;
  414.         mvals = (int *)realloc(mvals, CHUNK); /* grow it */
  415.      }
  416.      flag = 0;
  417.      mvals[i++] = numarg(str, &offset, base, &flag);
  418.      if(flag == 0) /* bad input */
  419.      {  free(mvals); /* give back vector */
  420.         return(0);
  421.      }
  422.       }  while( str[offset++] != '\0' );
  423.       mvals[0] = i - 1; /* number of numbers found */
  424.       result->_opt.mval = mvals;
  425.    }
  426.    return(1); /* ok */
  427. }
  428.  
  429. static list_file(f) char *f;
  430. {  FILE *in;
  431.    char c;
  432.  
  433.    if( (in = fopen(f, "r")) == NULL ) return;
  434.    while( (c = getc(in)) != EOF ) putc(c, stderr);
  435.    fclose(in);
  436. }
  437. SHAR_EOF
  438. fi # end of overwriting check
  439. if test -f 'cset.d'
  440. then
  441.     echo shar: will not over-write existing file "'cset.d'"
  442. else
  443. cat << \SHAR_EOF > 'cset.d'
  444. /* $Header: cset.d,v 1.1 89/03/03 12:53:22 np Exp $
  445.  * $Log:    cset.d,v $
  446. Revision 1.1  89/03/03  12:53:22  np
  447. Initial revision
  448.  
  449.  */
  450.  
  451. /* defines for cset source files */
  452.  
  453. /*
  454.  * Copyright (C) 1985-1989 Nigel Perry
  455.  *
  456.  * This program is distributed without any warranty.
  457.  * The author and and distributors accept no resposibility
  458.  * to anyone for the consequence of using this program or
  459.  * whether it serves any particular purpose or works at all,
  460.  * unless they say so in writing.
  461.  *
  462.  * Everyone is granted permission to copy, modify and
  463.  * redistribute this program, but only provided that:
  464.  *
  465.  * 1) They do so without financial or material gain.
  466.  *
  467.  * 2) The copyright notice and this notice are preserved on
  468.  *    all copies.
  469.  *
  470.  * 3) The original source is preserved in any redistribution.
  471.  *
  472.  * I hope you find this program useful!
  473.  *
  474.  */
  475.  
  476. #define TAB1 5 /* offset for first option in table */
  477. #define TAB2 40 /* offset of 2nd option in table */
  478. #define TAB3 25 /* offset to help text */
  479.  
  480. #define MVAL_BIT (MVAL_KWD ^ NVAL_KWD) /* =0x4000 */
  481. #define PRIV_BIT (PRIV_KWD ^ HIDE_KWD) /* =0x20000 */
  482. #define BASE_MASK 0xFF /* base of numeric keyword */
  483. SHAR_EOF
  484. fi # end of overwriting check
  485. if test -f 'cset.h'
  486. then
  487.     echo shar: will not over-write existing file "'cset.h'"
  488. else
  489. cat << \SHAR_EOF > 'cset.h'
  490. /* $Header: cset.h,v 1.1 89/03/03 12:53:23 np Exp $
  491.  * $Log:    cset.h,v $
  492.  * Revision 1.1  89/03/03  12:53:23  np
  493.  * Initial revision
  494.  * 
  495.  */
  496.  
  497. /*
  498.  * Copyright (C) 1985-1989 Nigel Perry
  499.  *
  500.  * This program is distributed without any warranty.
  501.  * The author and and distributors accept no resposibility
  502.  * to anyone for the consequence of using this program or
  503.  * whether it serves any particular purpose or works at all,
  504.  * unless they say so in writing.
  505.  *
  506.  * Everyone is granted permission to copy, modify and
  507.  * redistribute this program, but only provided that:
  508.  *
  509.  * 1) They do so without financial or material gain.
  510.  *
  511.  * 2) The copyright notice and this notice are preserved on
  512.  *    all copies.
  513.  *
  514.  * 3) The original source is preserved in any redistribution.
  515.  *
  516.  * I hope you find this program useful!
  517.  *
  518.  */
  519.  
  520. /* a type definition for the options table */
  521. typedef struct { char *_opt_pat; int _opt_types; char *_opt_help } _opt_entry;
  522. #define OPTIONS _opt_entry _optable[] =
  523. /* the end of list marker for above defn */
  524. #define OPT_END { (char *)0, 0, (char *)0 }
  525. /* end of list marker including details of an info file */
  526. #define OPT_HELP(act, str) { (char *)0, act, str }
  527. /* empty help message constant */
  528. #define NOHELP (char *)0
  529. /* 'actions' for OPT_HELP, must be != 0 */
  530. #define OPT_LIST 1
  531. #define OPT_EXEC 2
  532.  
  533. /* the option types */
  534. #define BLNK_KWD 0x00100 /*  keyword                                     */
  535. #define PLUS_KWD 0x00200 /* +keyword                                     */
  536. #define DASH_KWD 0x00400 /* -keyword                                     */
  537. #define COMM_KWD 0x00800 /*  keyword - only recognised if command (first)*/
  538. #define SVAL_KWD 0x01000 /*  keyword=string                              */
  539. #define NVAL_KWD 0x02000 /*  keyword=number - 0ddd => oct, 0xddd => hex  */
  540. #define MVAL_KWD 0x06000 /*  keyword=number,...,number                   */
  541. #define DVAL_KWD 0x0200a /*  keyword=decimal                             */
  542. #define MDVL_KWD 0x0600a /*  keyword=decimal,...,decimal                 */
  543. #define OVAL_KWD 0x02008 /*  keyword=octal                               */
  544. #define MOVL_KWD 0x06008 /*  keyword=octal,...,octal                     */
  545. #define HVAL_KWD 0x02010 /*  keyword=hex                                 */
  546. #define MHVL_KWD 0x06010 /*  keyword=hex,...,hex                         */
  547. #define HIDE_KWD 0x10000 /*  Hidden keyword - not shown by d_optable()   */
  548. #define PRIV_KWD 0x30000 /*  Priviledged keyword - only for su's         */
  549.  
  550. /* _cset() returns a pointer to an array of... */
  551. typedef struct { int _opt_num;
  552.          char _opt_type;
  553.          union { int nval; /* numeric value */
  554.              int *mval; /* ptr to vec of values */
  555.              char *sval; /* string value */
  556.                } _opt;
  557.           } _opt_desc;
  558.  
  559. extern _opt_desc *_cset();
  560.  
  561. /* defn for environ var to control use of keyword arguments */
  562. #define AS_ENVAR "ArgStyle"
  563. #define AS_KWD   "keyword"    /* arg style is keyword */
  564. #define AS_ORG   "original"   /* arg style as supplied */
  565. SHAR_EOF
  566. fi # end of overwriting check
  567. if test -f 'd_optable.c'
  568. then
  569.     echo shar: will not over-write existing file "'d_optable.c'"
  570. else
  571. cat << \SHAR_EOF > 'd_optable.c'
  572. static char rcsid[] = "$Header: d_optable.c,v 1.1 89/03/03 12:53:23 np Exp $";
  573. /* $Log:    d_optable.c,v $
  574.  * Revision 1.1  89/03/03  12:53:23  np
  575.  * Initial revision
  576.  * 
  577.  */
  578.  
  579. /*
  580.  * Copyright (C) 1985-1989 Nigel Perry
  581.  *
  582.  * This program is distributed without any warranty.
  583.  * The author and and distributors accept no resposibility
  584.  * to anyone for the consequence of using this program or
  585.  * whether it serves any particular purpose or works at all,
  586.  * unless they say so in writing.
  587.  *
  588.  * Everyone is granted permission to copy, modify and
  589.  * redistribute this program, but only provided that:
  590.  *
  591.  * 1) They do so without financial or material gain.
  592.  *
  593.  * 2) The copyright notice and this notice are preserved on
  594.  *    all copies.
  595.  *
  596.  * 3) The original source is preserved in any redistribution.
  597.  *
  598.  * I hope you find this program useful!
  599.  *
  600.  */
  601.  
  602. #include <stdio.h>
  603. #include "cset.h"
  604. #include "cset.d"
  605.  
  606. #define next continue
  607.  
  608. /* Pretty print an _optable - or try to, boring ol' stdio!
  609.  
  610.    Adapted from d.optab - written in the REAL language B Plus!
  611.  
  612.    args:
  613.       withelp - true is want long form with short desc
  614.       table   - the table to be printed, 0 => _optable
  615.       hidden  - if true, the 'hidden' options are also printed
  616.  */
  617.  
  618. d_optable(withelp, table, hidden) int withelp, hidden; _opt_entry *table;
  619. {  extern _opt_entry _optable[];
  620.    _opt_entry *p;
  621.    int c, n, flip, column;
  622.    char *left, *right;
  623.  
  624.    /* set default table */
  625.    if( table == (_opt_entry *)0 ) table = _optable;
  626.  
  627.    p = table;
  628.    flip = c = n = 0;
  629.    /* count command and option names */
  630.    while(p->_opt_pat) if((p++)->_opt_types == COMM_KWD) c++; else n++;
  631.    if(c > 1) /* only list command names if > 1 */
  632.    {  fputs("Command Names:", stderr);
  633.       p = table;
  634.       while(p->_opt_pat)
  635.       {  if(p->_opt_types == COMM_KWD)
  636.      {  if(withelp)
  637.         {  fprintf(stderr, "\n%*s%s%*s%s", TAB1, "", p->_opt_pat,
  638.           TAB3 - (strlen(p->_opt_pat) + TAB1), "",
  639.           p->_opt_help == NOHELP ? "(see manual)" : p->_opt_help);
  640.         }
  641.         else
  642.        {  fprintf(stderr, "%sc%*s%s", flip ? (char *)0 : "\n",
  643.           flip ? (TAB2 - column) : TAB1, "", p->_opt_pat);
  644.            if(flip = !flip) column = TAB1 + strlen(p->_opt_pat);
  645.         }
  646.      }
  647.      p++;
  648.       }
  649.       fputc('\n', stderr);
  650.       if(!n) return;
  651.    }
  652.    fputs("Options:", stderr);
  653.    flip = 0;
  654.    p = table; p--;
  655.    while( (++p)->_opt_pat )
  656.    {  if(p->_opt_types == COMM_KWD) next;
  657.       /* if HIDE and not hidden don't print it! */
  658.       if( (p->_opt_types & HIDE_KWD) && !hidden) next;
  659.       _opt_adorn(p->_opt_types, &left, &right);
  660.       if(withelp)
  661.       {  fprintf( stderr, "\n%*s%s%s%s%*s%s", TAB1, "", left, p->_opt_pat,
  662.           right,
  663.           TAB3 - (TAB1+strlen(left)+strlen(p->_opt_pat)+strlen(right)),
  664.           "", p->_opt_help == NOHELP ? "(see manual)" : p->_opt_help);
  665.       }
  666.       else
  667.       {  fprintf( stderr, "%s%*s", flip ? "" : "\n",
  668.           flip ? (TAB2 - column) : TAB1, "");
  669.      flip = !flip;
  670.      fprintf(stderr, "%s%s%s", left, p->_opt_pat, right);
  671.      if(flip)
  672.         column = TAB1 + strlen(left) + strlen(p->_opt_pat) + strlen(right);
  673.       }
  674.    }
  675.    fputc('\n', stderr);
  676. }
  677.  
  678. /* return strings to 'adorn' options */
  679. _opt_adorn(p, left, right) char **left, **right;
  680. {  static char *dpb[] = { (char *)0, "", "+", "[+]", "-", "[-]",
  681.               "(+|-)", "[+|-]" };
  682.    static char *s_val = { "=X" };
  683.    static char *m_val = { "=X,...,X" };
  684.    int c;
  685.    if(p == COMM_KWD)
  686.       *left = *right = "";
  687.    else if(c = p & (DASH_KWD | PLUS_KWD | BLNK_KWD))
  688.    {  *left = dpb[c >> 8];
  689.       *right = "";
  690.    }
  691.    else if(p  == SVAL_KWD)
  692.    {  *left = "";
  693.       s_val[1] = 's'; /* construct '=s' */
  694.       *right = s_val;
  695.    }
  696.    else /* numeric */
  697.    {  switch(p & 0xFF) /* find base */
  698.       {  case 0: c = 'n'; break;
  699.      case 8: c = 'o'; break;
  700.      case 10: c = 'd'; break;
  701.      case 16: c = 'x'; break;
  702.      default: /* somebody is being ackward... */
  703.         c = p & 0xFF;
  704.         c += c < 10 ? '0' : ('A' - 10);
  705.       }
  706.       *left = "";
  707.       if(p & MVAL_BIT)
  708.       {  m_val[1] = m_val[7] = c;
  709.      *right = m_val;
  710.       }
  711.       else
  712.       {  s_val[1] = c;
  713.      *right = s_val;
  714.       }
  715.    }
  716. }
  717. SHAR_EOF
  718. fi # end of overwriting check
  719. if test -f 'demo.c'
  720. then
  721.     echo shar: will not over-write existing file "'demo.c'"
  722. else
  723. cat << \SHAR_EOF > 'demo.c'
  724. /* demonstration of _cset */
  725.  
  726. #include "cset.h"
  727.  
  728. OPTIONS
  729. {  "DEMO",   COMM_KWD, "a command name",
  730.    "Tesco",  COMM_KWD, "another command name",
  731.    "String", SVAL_KWD, "an arbitrary string",
  732.    "Number", NVAL_KWD, "a number, base 8, 10 or 16",
  733.    "Tabs",   MVAL_KWD, "list of tab stops",
  734.    "Wide",   PLUS_KWD, "turn an option on",
  735.    "Multi",  PLUS_KWD | DASH_KWD | BLNK_KWD, "option may be +, - or plain string",
  736.    "Hidden", HIDE_KWD | NVAL_KWD | 19, "hidden numeric base 19 argument!",
  737.    OPT_HELP(OPT_EXEC, "more README")
  738. };
  739.  
  740. #define STRING    0
  741. #define    DEMO    1
  742. #define    TESCO    (DEMO+1)
  743. #define STR    (TESCO+1)
  744. #define NUM    (STR+1)
  745. #define TABS    (NUM+1)
  746. #define WIDE    (TABS+1)
  747. #define MULTI    (WIDE+1)
  748. #define HID    (MULTI+1)
  749.  
  750. /* I'm sure I should be able to do this in one go..., but the compiler barfs */
  751. char *d1[] = {"demo", "st=asda", (char *)0};
  752. char *d2[] = {"demo", "tesco", "num=0xd", "l=57", "+g", (char *)0};
  753. char *d3[] = {"tesco", "+w", "-mu", "+mt", "muti", (char *)0};
  754. char *d4[] = {"demo", "h=beef t=09,9,0x9", (char *)0};
  755. char *d5[] = {"demo", "==", (char *)0};
  756. char *d6[] = {"demo", "=hi", "=+", "=#", (char *)0};
  757.  
  758. char **Demos[] =
  759. {  d1, d2, d3, d4, d5, d6,
  760.    (char **)0
  761. };
  762.  
  763. execute(cmd, args)
  764. char *cmd, **args;
  765. {  if( fork() )
  766.       wait(0);
  767.    else
  768.       execv(cmd, args);
  769. };
  770.  
  771. main(argc, argv) char **argv;
  772. {  _opt_desc *parsed;
  773.    char ***dp, **cmd;
  774.    int *p, num;
  775.  
  776.    if(argc <= 1)
  777.    {  /* no arguments, do demos */
  778.       dp = Demos;
  779.       while(*dp != (char **)0)
  780.       {  printf("\n************************\nExecuting:");
  781.          cmd = *dp;
  782.          while(*cmd != (char *)0) printf(" %s", *cmd++);
  783.          printf("\nGives:\n\n");
  784.      execute("./demo", *dp++);
  785.       }
  786.       return;
  787.    }
  788.  
  789.    parsed = _cset(argv, 1);
  790.  
  791.    while( parsed->_opt_num != -1 )
  792.    {  switch( parsed->_opt_num ) 
  793.       {  case STRING:
  794.              printf("Arbitrary string option: %s\n", parsed->_opt.sval); break;
  795.          case DEMO:
  796.            break;
  797.          case TESCO:
  798.            printf("Called using alternative command name \"tesco\"\n"); break;
  799.          case NUM:
  800.            printf("Numeric value: 0%o, %d, 0x%x\n", parsed->_opt.nval, parsed->_opt.nval, parsed->_opt.nval); break;
  801.          case STR:
  802.            printf("String valued option: %s\n", parsed->_opt.sval); break;
  803.          case HID:
  804.            printf("Hidden keyword, number input in base 19 = %d in decimal\n", parsed->_opt.nval);
  805.          case MULTI:
  806.            printf("Option may be +, - or blank, this time it was: '%c'\n", parsed->_opt_type);
  807.            break;
  808.          case WIDE:
  809.            printf("Wide option turned on\n"); break;
  810.          case TABS:
  811.            p = parsed->_opt.mval;
  812.            printf("Muliple numeric valued keyword with %d values:", (num = *p++));
  813.             while(num--) printf(" %d", *p++);
  814.            printf("\n");
  815.            break;
  816.      default:    /* out of range - looks like an option but didn't match */
  817.            printf("Unknown option: %s\n", parsed->_opt.sval );
  818.  
  819.       }
  820.       parsed++;
  821.    }
  822. }
  823.    
  824. SHAR_EOF
  825. fi # end of overwriting check
  826. if test -f 'makefile'
  827. then
  828.     echo shar: will not over-write existing file "'makefile'"
  829. else
  830. cat << \SHAR_EOF > 'makefile'
  831. # $Header: makefile,v 1.1 89/03/03 12:53:24 np Exp $
  832. all:    cset.a demo
  833.  
  834. demo:    demo.c cset.a
  835.     cc -o demo demo.c cset.a
  836. cset.a: cset.o d_optable.o numarg.o abbrev.o not_kwd.o
  837.     ranlib cset.a
  838. cset.o: cset.c cset.d
  839.     cc -c cset.c
  840.     ar rv cset.a cset.o
  841. .c.o:;  cc -c $*.c
  842.     ar rv cset.a $*.o
  843.  
  844. cleanup:
  845.     rm *.o
  846. SHAR_EOF
  847. fi # end of overwriting check
  848. if test -f 'not_kwd.c'
  849. then
  850.     echo shar: will not over-write existing file "'not_kwd.c'"
  851. else
  852. cat << \SHAR_EOF > 'not_kwd.c'
  853. static char rcsid[] = "$Header: not_kwd.c,v 1.1 89/03/03 12:53:25 np Exp $";
  854. /* $Log:    not_kwd.c,v $
  855.  * Revision 1.1  89/03/03  12:53:25  np
  856.  * Initial revision
  857.  * 
  858.  */
  859.  
  860. /*
  861.  * Copyright (C) 1985-1989 Nigel Perry
  862.  *
  863.  * This program is distributed without any warranty.
  864.  * The author and and distributors accept no resposibility
  865.  * to anyone for the consequence of using this program or
  866.  * whether it serves any particular purpose or works at all,
  867.  * unless they say so in writing.
  868.  *
  869.  * Everyone is granted permission to copy, modify and
  870.  * redistribute this program, but only provided that:
  871.  *
  872.  * 1) They do so without financial or material gain.
  873.  *
  874.  * 2) The copyright notice and this notice are preserved on
  875.  *    all copies.
  876.  *
  877.  * 3) The original source is preserved in any redistribution.
  878.  *
  879.  * I hope you find this program useful!
  880.  *
  881.  */
  882.  
  883. /* do execv if AS_ENVAR not set to AS_KWD
  884.  * enables programs to switch the option format according to the environment setting
  885.  */
  886.  
  887. #include "cset.h"
  888.  
  889. not_kwd(object, args) register char *object, **args;
  890. {  register char *val;
  891.    extern char *getenv();
  892.  
  893.    if( (val = getenv(AS_ENVAR)) == (char *)0 || strcmp(val, AS_KWD) != 0 )
  894.       execv(object, args);
  895. }
  896. SHAR_EOF
  897. fi # end of overwriting check
  898. if test -f 'numarg.c'
  899. then
  900.     echo shar: will not over-write existing file "'numarg.c'"
  901. else
  902. cat << \SHAR_EOF > 'numarg.c'
  903. static char rcsid[] = "$Header: numarg.c,v 1.1 89/03/03 12:53:25 np Exp $";
  904. /* $Log:    numarg.c,v $
  905.  * Revision 1.1  89/03/03  12:53:25  np
  906.  * Initial revision
  907.  * 
  908.  */
  909.  
  910. /*
  911.  * Copyright (C) 1985-1989 Nigel Perry
  912.  *
  913.  * This program is distributed without any warranty.
  914.  * The author and and distributors accept no resposibility
  915.  * to anyone for the consequence of using this program or
  916.  * whether it serves any particular purpose or works at all,
  917.  * unless they say so in writing.
  918.  *
  919.  * Everyone is granted permission to copy, modify and
  920.  * redistribute this program, but only provided that:
  921.  *
  922.  * 1) They do so without financial or material gain.
  923.  *
  924.  * 2) The copyright notice and this notice are preserved on
  925.  *    all copies.
  926.  *
  927.  * 3) The original source is preserved in any redistribution.
  928.  *
  929.  * I hope you find this program useful!
  930.  *
  931.  */
  932.  
  933. #define repeat while(1)
  934.  
  935. /* convert string to number
  936.  * args:
  937.  *   str:    ptr to string to be parsed
  938.  *   poffset:   ptr to offset into str at which to start, will be updated on exit
  939.  *   base:    base of number to parse (2 -> 36), < 2 implies 0ddd -> oct, 0xddd -> hex, ddd -> dec
  940.  *   pflag:    ptr to flag, on input *pflag = 1 => skip leading junk (blanks always skipped)
  941.  *                 on exit  *pflag = 0 => failed to parse number
  942.  * return:
  943.  *   *pflag = 1 => number parsed
  944.  *   *pflag = 0 => 0, failed to parse
  945.  *
  946.  */
  947.  
  948. int numarg(str, poffset, base, pflag) register char *str;
  949.                       register int base, *poffset, *pflag;
  950. {  int neg, autobase;
  951.    register int val, dig;
  952.    char *start;
  953.    register char c;
  954.  
  955.    neg = autobase = val = 0;
  956.  
  957.    /* if base < 2, set oct/dec/hex */
  958.    if(base < 2) { base = 10; autobase = 1; }
  959.  
  960.    start = str; /* save start */
  961.    str += *poffset; /* add in starting offset */
  962.  
  963.    /* find first digit */
  964.    repeat
  965.    {  switch(c = *str++)
  966.       {  case ' ': continue; /* skip leading blanks */
  967.      case '-': neg = 1; /* flag negative */
  968.      case '+': if( (dig = getdig(*str++, base)) < 0 )
  969.            {  *poffset = str - start;
  970.               return(*pflag = 0); /* error */
  971.            }
  972.            break;
  973.      default: if( (dig = getdig(c, base)) >= 0 )
  974.              break; /* good digit */
  975.           if( *pflag == 1 ) continue; /* skip leading junk */
  976.           *poffset = str - start;
  977.           return(*pflag = 0); /* junk in de way */
  978.       }
  979.       break; /* kill repeat */
  980.    }
  981.  
  982.    /* if get here, dig is first digit & neg flag be set */
  983.    if(dig == 0 && autobase) /* check for oct/hex */
  984.    {  if( (c = *str) == 'x' || c == 'X' ) /* hex */
  985.       {  str++; /* skip x */
  986.      base = 16;
  987.       }
  988.       else
  989.      base = 8;
  990.    }
  991.    else
  992.       val = dig;
  993.  
  994.    /* read in till non-digit */
  995.    while( (dig = getdig(*str++, base)) >= 0 ) val = val * base + dig;
  996.  
  997.    *poffset = str - start - 1;
  998.    *pflag = 1; /* good return */
  999.    return(neg ? -val : val);
  1000. }
  1001.  
  1002. static getdig(c, base) register int c, base;
  1003. {  if( c >= '0' && c <= '9' )  /* this SHOULD be a case... B rules! */
  1004.       return( (c -= '0') < base ? c : -1 );
  1005.    else if( c >= 'a' && c <= 'z' )
  1006.       return( (c += 10 - 'a') < base ? c : -1 );
  1007.    else if(c >= 'A' && c <= 'Z' )
  1008.       return( (c += 10 - 'A') < base ? c : -1);
  1009.    else return(-1);
  1010. }
  1011. SHAR_EOF
  1012. fi # end of overwriting check
  1013. #    End of shell archive
  1014. exit 0
  1015. ---
  1016. Nigel Perry                                  Department of Computing
  1017.                                              Imperial College
  1018. Janet: np@uk.ac.ic.doc                       London
  1019. DARPA: np%uk.ac.ic.doc@ucl-cs                SW7
  1020. Uucp:  np@icdoc.UUCP, ukc!icdoc!np           England
  1021.